﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace Techappy.Silverlight.Toolkit.Primitives
{
    [TemplatePart(Name = ItemsPanelName, Type = typeof(Panel))]
    [TemplatePart(Name = EmptyContentName, Type = typeof(ContentControl))]
    [TemplatePart(Name = CenteringTransformName, Type = typeof(TranslateTransform))]
    public class LoopingSelector : Control
    {
        #region Public

        #region DataSource

        public ILoopingSelectorDataSource DataSource
        {
            get { return (ILoopingSelectorDataSource)GetValue(DataSourceProperty); }
            set
            {
                if (DataSource != null)
                    DataSource.SelectionChanged -= value_SelectionChanged;

                SetValue(DataSourceProperty, value);

                if (value != null)
                    value.SelectionChanged += value_SelectionChanged;
            }
        }
        void value_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (!IsReady) return;

            if (!_isSelecting && e.AddedItems.Count == 1)
            {
                object selection = e.AddedItems[0];

                foreach (LoopingSelectorItem child in _itemsPanel.Children)
                {
                    if (child.DataContext == selection)
                    {
                        wrapper_Click(child, null);
                        return;
                    }
                }
                FirstArrange();
            }
        }
        public static readonly DependencyProperty DataSourceProperty =
            DependencyProperty.Register("DataSource", typeof(ILoopingSelectorDataSource), typeof(LoopingSelector), new PropertyMetadata(null, OnDataModelChanged));

        private static void OnDataModelChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            LoopingSelector picker = (LoopingSelector)obj;
            picker.FirstArrange();
        }

        #endregion

        #region ItemTemplate

        public DataTemplate ItemTemplate
        {
            get { return (DataTemplate)GetValue(ItemTemplateProperty); }
            set { SetValue(ItemTemplateProperty, value); }
        }
        public static readonly DependencyProperty ItemTemplateProperty =
            DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(LoopingSelector), new PropertyMetadata(null));

        #endregion

        #region Orientation

        public Orientation Orientation
        {
            get { return (Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }
        public static readonly DependencyProperty OrientationProperty =
            DependencyProperty.Register("Orientation", typeof(Orientation), typeof(LoopingSelector), new PropertyMetadata(Orientation.Vertical));

        #endregion

        /// <summary>
        /// 选中元素的位置
        /// </summary>
        public Point SelectedItemPosition { get; set; }
        /// <summary>
        /// 设定一个空白区域
        /// 空白区域位于选中元素的右边（横向）或者下边（纵向）
        /// 此控件的DataContext被设置为SelectedItem
        /// 当元素横向排列时，WidthOrHeight代表宽带，反之代表高度
        /// </summary>
        public Control EmptyContent { get; set; }
        /// <summary>
        /// 预留区域的宽度或者高度
        /// </summary>
        public double EmptyWidthOrHeight { get; set; }

        public object SelectedItem
        {
            get
            {
                if (_selectedItem == null) return null;

                return _selectedItem.DataContext;
            }
        }
        public Size ItemSize { get; set; }
        public Thickness ItemMargin { get; set; }

        #endregion

        #region 私有变量

        private const string ItemsPanelName = "ItemsPanel";
        private const string EmptyContentName = "EmptyContent";
        private const string CenteringTransformName = "CenteringTransform";

        private static readonly Duration _selectDuration = new Duration(TimeSpan.FromMilliseconds(500));
        private readonly IEasingFunction _selectEase = new ExponentialEase() { EasingMode = EasingMode.EaseInOut };

        private static readonly Duration _panDuration = new Duration(TimeSpan.FromMilliseconds(100));
        private readonly IEasingFunction _panEase = new ExponentialEase();

        private Storyboard _panelStoryboard;

        private Panel _itemsPanel;
        private ContentControl _emptyContent;
        private TranslateTransform _centeringTransform; //对SelectedItem的定位

        private bool _isSelecting;
        private LoopingSelectorItem _selectedItem;

        private double _minimumPanelScroll = float.MinValue;
        private double _maximumPanelScroll = float.MaxValue;

        private bool _isAnimating;
        private bool IsReady
        {
            get { return ActualHeight > 0 && DataSource != null && _itemsPanel != null; }
        }

        private double ActualItemWidth { get { return Padding.Left + Padding.Right + ItemSize.Width; } }
        private double ActualItemHeight { get { return Padding.Top + Padding.Bottom + ItemSize.Height; } }

        #endregion

        public LoopingSelector()
        {
            DefaultStyleKey = typeof(LoopingSelector);
            SizeChanged += new SizeChangedEventHandler(LoopingSelector_SizeChanged);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _emptyContent = GetTemplateChild(EmptyContentName) as ContentControl ?? new ContentControl();
            _itemsPanel = GetTemplateChild(ItemsPanelName) as Panel ?? new Canvas();
            _centeringTransform = GetTemplateChild(CenteringTransformName) as TranslateTransform ?? new TranslateTransform();
        }

        /// <summary>
        /// 自适应调整选中元素的位置
        /// </summary>
        void LoopingSelector_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (Orientation == System.Windows.Controls.Orientation.Horizontal)
            {
                if (SelectedItemPosition != null)
                    _centeringTransform.X = Math.Round(ActualItemWidth / 2 + SelectedItemPosition.X);
                else
                    _centeringTransform.X = Math.Round(e.NewSize.Width / 2);
            }
            else
            {
                if (SelectedItemPosition != null) //从指定位置开始
                    _centeringTransform.Y = Math.Round(ActualItemHeight / 2 + SelectedItemPosition.Y);
                else  //默认从中间开始
                    _centeringTransform.Y = Math.Round(e.NewSize.Height / 2);
            }
            Clip = new RectangleGeometry() { Rect = new Rect(0, 0, e.NewSize.Width, e.NewSize.Height) };

            _itemsPanel.Children.Clear();
            FirstArrange();
        }
        private void FirstArrange()
        {
            if (!IsReady || DataSource.SelectedItem == null) return;
            _selectedItem = CreateAndAddItem(_itemsPanel, DataSource.SelectedItem);
            if (_selectedItem == null) return;

            double emptySpace = EmptyContent != null ? EmptyWidthOrHeight : 0;
            double actualItemWidthOrHeight, YX;

            if (Orientation == System.Windows.Controls.Orientation.Horizontal)
            {
                _selectedItem.Transform.X = -ActualItemWidth / 2;
                YX = _selectedItem.Transform.Y = (ActualHeight - ActualItemHeight) / 2;
                actualItemWidthOrHeight = ActualItemWidth;

                if (EmptyContent != null)
                {
                    _emptyContent.Content = EmptyContent;
                    TranslateTransform trans = new TranslateTransform();
                    trans.X = _centeringTransform.X + ActualItemWidth / 2;
                    trans.Y = YX;
                    _emptyContent.RenderTransform = trans;
                }
            }
            else
            {
                _selectedItem.Transform.Y = -ActualItemHeight / 2;//Select状态的元素的中心在可视区域中间位置，所以Y坐标上移半个元素的高度
                YX = _selectedItem.Transform.X = (ActualWidth - ActualItemWidth) / 2;//水平平移到中间位置
                actualItemWidthOrHeight = ActualItemHeight;

                if (EmptyContent != null)
                {
                    _emptyContent.Content = EmptyContent;
                    TranslateTransform trans = new TranslateTransform();
                    trans.Y = _centeringTransform.Y + ActualItemHeight / 2;
                    trans.X = YX;
                    _emptyContent.RenderTransform = trans;
                }
            }
            _selectedItem.SetState(LoopingSelectorItem.State.Selected, false);

            object currentItem = DataSource.GetPrevious(DataSource.SelectedItem);
            LoopingSelectorItem pre = CreateAndAddItem(_itemsPanel, currentItem);
            _selectedItem.Previous = pre;
            double currentTransform = 0 - actualItemWidthOrHeight / 2;//-40;
            while (pre != null)
            {
                pre.Transform.Y = currentTransform = currentTransform - actualItemWidthOrHeight;
                pre.Transform.X = YX;
                pre.LTOrRB = true;

                currentItem = DataSource.GetPrevious(currentItem);
                var temp = CreateAndAddItem(_itemsPanel, currentItem);
                pre.Previous = temp;
                pre = temp;
            }

            currentItem = DataSource.GetNext(DataSource.SelectedItem);
            LoopingSelectorItem next = CreateAndAddItem(_itemsPanel, currentItem);
            _selectedItem.Next = next;
            currentTransform = actualItemWidthOrHeight / 2 + emptySpace;
            while (next != null)
            {
                next.Transform.Y = currentTransform;
                next.Transform.X = YX;
                next.LTOrRB = false;
                currentTransform = currentTransform + actualItemWidthOrHeight;

                currentItem = DataSource.GetNext(currentItem);
                var temp = CreateAndAddItem(_itemsPanel, currentItem);
                next.Next = temp;
                next = temp;
            }
        }
        private LoopingSelectorItem CreateAndAddItem(Panel parent, object content)
        {
            if (content == null) return null;

            LoopingSelectorItem wrapper = new LoopingSelectorItem();
            wrapper.ContentTemplate = this.ItemTemplate;
            wrapper.Width = ItemSize.Width;
            wrapper.Height = ItemSize.Height;
            wrapper.Padding = ItemMargin;

            wrapper.Click += new EventHandler<EventArgs>(wrapper_Click);
            wrapper.DataContext = wrapper.Content = content;

            //直接将子元素填充进Canvas，没有设置Canv.Left和Top，所以全部位于（0,0）位置
            //真正的元素位置完全由TranslateTransform控制
            parent.Children.Add(wrapper);
            wrapper.ApplyTemplate();
            return wrapper;
        }

        void wrapper_Click(object sender, EventArgs e)
        {
            if (sender == _selectedItem || _isAnimating) return;

            if (_selectedItem != null)
                _selectedItem.SetState(LoopingSelectorItem.State.Expanded, true);

            LoopingSelectorItem item = sender as LoopingSelectorItem;
            if (_selectedItem != item)
            {
                _selectedItem = item;
                Dispatcher.BeginInvoke(() =>
                {
                    _isSelecting = true;
                    DataSource.SelectedItem = item.DataContext;
                    _isSelecting = false;
                });
            }

            _selectedItem.SetState(LoopingSelectorItem.State.Selected, true);

            TranslateTransform transform = item.Transform;
            if (transform != null)
            {
                double move;
                if (Orientation == System.Windows.Controls.Orientation.Horizontal)
                {
                    move = -(transform.X - Math.Round(item.ActualWidth / 2));
                }
                else
                {
                    move = -(transform.Y + Math.Round(item.ActualHeight / 2));
                }

                if (move < 0)
                {
                    if (EmptyContent != null) move = move + EmptyWidthOrHeight;
                }

                if (0 != move)
                {
                    AnimatePanel(item, _selectDuration, _selectEase, move);
                }
            }
        }
        //调整位置
        private void AnimatePanel(LoopingSelectorItem sender, Duration duration, IEasingFunction ease, double to)
        {
            _panelStoryboard = new Storyboard();
            _panelStoryboard.Completed += (obj, arg) =>
            {
                _isAnimating = false;
            };

            // Be sure not to run past the first or last items
            double newTo = Math.Max(_minimumPanelScroll, Math.Min(_maximumPanelScroll, to));
            if (to != newTo)
            {
                // 调整动画时间
                double originalDelta = Math.Abs(-to);
                double modifiedDelta = Math.Abs(-newTo);
                double factor = modifiedDelta / originalDelta;

                duration = new Duration(TimeSpan.FromMilliseconds(duration.TimeSpan.Milliseconds * factor));
                to = newTo;
            }

            foreach (LoopingSelectorItem item in _itemsPanel.Children)
            {
                DoubleAnimation da = new DoubleAnimation();
                Storyboard.SetTarget(da, item.Transform);
                da.Duration = duration;
                da.EasingFunction = ease;
                da.From = item.Transform.Y;

                if (Orientation == System.Windows.Controls.Orientation.Horizontal)
                    Storyboard.SetTargetProperty(da, new PropertyPath("X"));
                else
                    Storyboard.SetTargetProperty(da, new PropertyPath("Y"));

                if (to > 0) //向下或右移动
                {
                    if (item.Transform.Y < 0 && item.Transform.Y > -ActualItemHeight / 2 - to)
                        da.To = item.Transform.Y + to + EmptyWidthOrHeight;
                    else
                        da.To = item.Transform.Y + to;
                }

                if (to < 0)//向上或左移动
                {
                    if (item.Transform.Y > 0 && item.Transform.Y < ActualItemHeight / 2 - to + (EmptyContent == null ? 0 : EmptyWidthOrHeight))
                        da.To = item.Transform.Y + to - EmptyWidthOrHeight;
                    else
                        da.To = item.Transform.Y + to;
                }

                _panelStoryboard.Children.Add(da);
            }

            _panelStoryboard.Begin();
            _panelStoryboard.SeekAlignedToLastTick(TimeSpan.Zero);

            _isAnimating = true;
        }
    }
}
